-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at: http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local class = require 'mc.class'
local c_handler_base = require 'bma.handles.handler_base'
local c_handler_eth = require 'bma.handles.handler_eth'
local c_handler_fc = require 'bma.handles.handler_fc'
local c_optical_module = require 'device.class.optical_module'
local log = require 'mc.logging'
local singleton = require 'mc.singleton'

local get_odata_id = c_handler_base.get_odata_id

local function speed_str_to_mbps(speed_str)
    local bps_str, t = speed_str:match('(%d+)([GM])')
    if not bps_str then
        log:error('handle bma optical speed failed: invalid speed_str=%s', speed_str)
        return 0
    end

    local bps = tonumber(bps_str)
    if t == 'M' then
        return bps
    elseif t == 'G' then
        return bps * 1000
    else
        log:error('handle bma optical speed failed: unknown unit %s, speed_str=%s', t, speed_str)
        return bps
    end
end

local function parser_speed_array(speed)
    local ret = {}
    for i, speed_str in ipairs(speed) do
        ret[i] = speed_str_to_mbps(speed_str)
    end
    return ret
end

local c_handler_optical = class(c_handler_base)

function c_handler_optical:ctor()
end

function c_handler_optical:init()
    self:regist_odata_type('OemEthernetInterfaceSff')
    self:regist_odata_type('OemInfiniBandSff')
    self:regist_odata_type('OemFCSff')

    self:regist_class_type(c_optical_module)

    c_handler_eth.get_instance().on_need_update:on(function(object, path)
        local optical_module = object and object:get_optical_module() or nil
        self:match_resource(optical_module, path)
    end)

    c_handler_fc.get_instance().on_need_update:on(function(object, path)
        local optical_module = object and object:get_optical_module() or nil
        self:match_resource(optical_module, path)
    end)

    c_optical_module.on_add_object:on(function(object)
        self:match_resource(object)
    end)
end

function c_handler_optical:find_object(_, data)
    local port_bma_id = self.get_parent_path(get_odata_id(data))
    if not port_bma_id then
        return nil
    end

    local eth_obj = c_handler_eth.get_instance():get_object(port_bma_id) or
        c_handler_fc.get_instance():get_object(port_bma_id)
    if not eth_obj then
        return nil
    end

    return eth_obj:get_optical_module()
end

function c_handler_optical:update_optical(op, data)
    if op and op:check_port_workload_type() then
        return
    end
    op.Presence = 1
    if data.VendorName ~= nil then
        op.Manufacturer = data.VendorName
    end
    if data.PartNumber ~= nil then
        op.PartNumber = data.PartNumber
    end
    if data.SerialNumber ~= nil then
        op.SerialNumber = data.SerialNumber
    end
    if data.ManufactureDate ~= nil then
        local ok, _ = pcall(function ()
            op.ProductionDate = string.format('20%s-%s-%s',
                string.sub(data.ManufactureDate, 1, 2),
                string.sub(data.ManufactureDate, 3, 4),
                string.sub(data.ManufactureDate, 5, 6))
        end)
        if not ok then
            op.ProductionDate = data.ManufactureDate
        end
    end
    if data.TransceiverType ~= nil then
        op.TransceiverType = data.TransceiverType
    end
    if data.Wavelength ~= nil then
        op.WaveLengthNanometer = data.Wavelength
    end
    if data.MediumMode ~= nil then
        op.FiberConnectionType = data.MediumMode
    end
    if data.speed ~= nil then
        op.SupportedSpeedsMbps = parser_speed_array(data.speed)
    end
    if data.type ~= nil then
        op.Type = data.type
    end
    if data.TypeMatch ~= nil then
        op.TypeMatch = data.TypeMatch
    end
    if data.SpeedMatch ~= nil then
        op.SpeedMatch = data.SpeedMatch
    end
    if data.ChannelNum ~= nil then
        op.ChannelNum = data.ChannelNum
    end
    op:update_asset_data_info()
end

function c_handler_optical:reset()
    for object, _ in pairs(self.objects) do
        object:reset_bma_info()
    end
end

function c_handler_optical:add(_, data, object)
    self:update_optical(object, data)
end

function c_handler_optical:delete(_, _, object)
    object:reset_bma_info()
    object:reset_bma_stats()
end

function c_handler_optical:update(path, data, object)
    self:update_optical(object, data)
end

return singleton(c_handler_optical)
